package tools;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;

public class Gauss {
	public static double gaussian(double x, double y, double sigma) {
		double sgSq = 2*sigma*sigma;
		return Math.exp(-(((x*x)+(y*y))/sgSq));
	}
	public static double[] buildGaussianKernel(int n, double sigma, double[] fillme) {
		int n2 = n/2;
		for (int i = 0; i<fillme.length; i++) {
			int x = (i%n)-n2, y = i/n-n2;
			fillme[i] = gaussian(x, y, sigma);
		}
		
		return fillme;
	}
	public static double gaussian1d(double x, double sigma) {
		double sgSq = 2*sigma*sigma;
		return Math.exp(-((x*x)/sgSq));
	}
	public static double[] gaussX(double[] lum, int w, int h, double sigma, double[] fillme) {
		final double kernel[] = new double[(int)Math.ceil(3*sigma)], n2 = kernel.length/2., temp[] = new double[lum.length], temp2[] = new double[lum.length];
		int in2 = (int)n2;
		double sum = 0;
		for (int i = 0; i<kernel.length; i++) {
			sum += kernel[i] = gaussian1d(i-n2, sigma);
		}
		for (int i = 0; i<kernel.length; i++) {
			kernel[i] /= sum;
		}
		for (int i = 0; i < lum.length; i++) {
			sum = 0;
			int x = i%w, y = i/w;
			for (int j = 0; j < kernel.length; j++) {
				sum += lum[(lum.length+((w+x+j-in2)%w)+y*w)%lum.length]*kernel[j];
			}
			fillme[i] = sum;
		}
		
 		return fillme;
	}
	public static double[] gaussY(double[] lum, int w, int h, double sigma, double[] fillme) {
		final double kernel[] = new double[(int)Math.ceil(3*sigma)], n2 = kernel.length/2., temp[] = new double[lum.length], temp2[] = new double[lum.length];
		int in2 = (int)n2;

		double sum = 0;
		for (int i = 0; i<kernel.length; i++) {
			sum += kernel[i] = gaussian1d(i-n2, sigma);
		}
		for (int i = 0; i<kernel.length; i++) {
			kernel[i] /= sum;
		}
		for (int i = 0; i < lum.length; i++) {
			sum = 0;
			int x = i%w, y = i/w;
			for (int j = 0; j < kernel.length; j++) {
				sum += lum[(lum.length+(x+(y+h+j-in2)*w))%lum.length]*kernel[j];
			}
			fillme[i] = sum;
		}

		return fillme;
	}
	public static int[] gaussXY(int[] lum, int w, int h, double sigma, int[] fillme) {
		int[] secondPass = gaussX(lum, w, h, sigma, new int[fillme.length]);
		fillme = gaussY(secondPass, w, h, sigma, fillme);
		return fillme;
	}
	public static double[] gaussXY(double[] lum, int w, int h, double sigma, double[] fillme) {
		double[] secondPass = gaussX(lum, w, h, sigma, new double[fillme.length]);
		fillme = gaussY(secondPass, w, h, sigma, fillme);
		return fillme;
	}
	public static int[] gaussX(int[] lum, int w, int h, double sigma, int[] fillme) {
		final double kernel[] = new double[(int)Math.ceil(3*sigma)], n2 = kernel.length/2., temp[] = new double[lum.length], temp2[] = new double[lum.length];
		int in2 = (int)n2;
		double sum = 0;
		for (int i = 0; i<kernel.length; i++) {
			sum += kernel[i] = gaussian1d(i-n2, sigma);
		}
		for (int i = 0; i<kernel.length; i++) {
			kernel[i] /= sum;
		}
		for (int i = 0; i < lum.length; i++) {
			sum = 0;
			int x = i%w, y = i/w;
			for (int j = 0; j < kernel.length; j++) {
				sum += lum[(lum.length+((w+x+j-in2)%w)+y*w)%lum.length]*kernel[j];
			}
			fillme[i] = (int)sum;
		}
		
 		return fillme;
	}
	public static int[] gaussY(int[] lum, int w, int h, double sigma, int[] fillme) {
		final double kernel[] = new double[(int)Math.ceil(3*sigma)], n2 = kernel.length/2., temp[] = new double[lum.length], temp2[] = new double[lum.length];
		int in2 = (int)n2;

		double sum = 0;
		for (int i = 0; i<kernel.length; i++) {
			sum += kernel[i] = gaussian1d(i-n2, sigma);
		}
		for (int i = 0; i<kernel.length; i++) {
			kernel[i] /= sum;
		}
		for (int i = 0; i < lum.length; i++) {
			sum = 0;
			int x = i%w, y = i/w;
			for (int j = 0; j < kernel.length; j++) {
				sum += lum[(lum.length+(x+(y+h+j-in2)*w))%lum.length]*kernel[j];
			}
			fillme[i] = (int)sum;
		}

		return fillme;
	}
 	public static void main(String[] args) throws Exception {
		int n = 65, n2 = n/2;
		double sigma = 8.;
		double[] kernel = new double[n];
		double sum = 0; 
		
		for (int i = 0; i<kernel.length; i++) {
			sum += kernel[i] = gaussian1d(i-n2, sigma);
		}
		
		for (int i = 0; i<kernel.length; i++) {
			kernel[i] /= sum;
		}
		
		final BufferedImage bi = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("res/kermit001.jpg"));
		final int w = bi.getWidth(), h = bi.getHeight();
		int[] pixels = bi.getRGB(0, 0, w, h, new int[w*h], 0, w);
		int[] lum = ColorConversions.getFastLuminance(pixels, new int[w*h]);
		
//		double[] toGaussian = new double[w*h];
//		double[] toGaussian2 = new double[w*h];
		
		pixels = gaussX(lum, w, h, sigma, pixels);
		lum = gaussY(pixels, w, h, sigma, lum);
		
		
		pixels = ColorConversions.buildRGB(lum, lum, lum, pixels);
		bi.setRGB(0, 0, w, h, pixels, 0, w);
		
		JFrame ff = new JFrame();
		ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ff.add(new JComponent() {
			{setPreferredSize(new Dimension(bi.getWidth(), bi.getHeight()));}
			@Override
			public void paint(Graphics g) {
				g.drawImage(bi, 0, 0, this);
				super.paint(g);
			}
		});
		
		ff.pack();
		ff.setVisible(true);
		
	}
	public static void main2(String[] args) {
		int n = 257, n2 = n/2;
		System.out.println(n2);
		double[] kernel = new double[n*n];
		double sigma = n2/3.;
		
		for (int i = 0; i<kernel.length; i++) {
			int x = (i%n)-n2, y = i/n-n2;
			kernel[i] = gaussian(x, y, sigma);
		}
		
		int[] pixels = new int[n*n];
		int[] pixels2 = new int[n*n];
		final BufferedImage bi = new BufferedImage(n, n, BufferedImage.TYPE_INT_ARGB);
		
		for (int i = 0; i < pixels.length; i++) {
			pixels[i] = (int)(255*kernel[i]/kernel[n2+n2*n]);
		}
		ColorConversions.buildRGB(pixels, pixels, pixels, pixels2);
		bi.setRGB(0, 0, n, n, pixels2, 0, n);
		
		JFrame ff = new JFrame();
		ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ff.add(new JComponent() {
			{setPreferredSize(new Dimension(bi.getWidth(), bi.getHeight()));}
			@Override
			public void paint(Graphics g) {
				g.drawImage(bi, 0, 0, this);
				super.paint(g);
			}
		});
		
		ff.pack();
		ff.setVisible(true);
	}
}
